# coding=UTF-8
import os
import time

base_path = os.getcwd().split('\\StockMonitor')[0] + '\\StockMonitor\\'
stock_path = base_path.split('\\stock')[0] + '\\stock\\'

from utils import json_util, k_util


# kdl使用方法
# 1.K与D值永远介于0到100之间。D大于80时，行情呈现超买现象。D小于20时，行情呈现超卖现象。
# 2.上涨趋势中，K值小于D值，K线向上突破D线时，为买进信号。下跌趋势中，K值大于D值，K线向下跌破D线时，为卖出信号。
# 3.KD指标不仅能反映出市场的超买超卖程度，还能通过交叉突破发出买卖信号。
# 4.KD指标不适于发行量小、交易不活跃的股票，但是KD指标对大盘和热门大盘股有极高准确性。
# 5.当随机指标与股价出现背离时，一般为转势的信号。
# 6.K值和D值上升或者下跌的速度减弱，倾斜度趋于平缓是短期转势的预警信号。

# kdl指标分析
# 面对变幻莫测的中国股市行情，学会并准确利用相关技术指标进行股票分析就显得很有必要，它可以帮助我们更好地抓住规律，更好地着手操作。
# 今天就给大家从五个方面讲解一下炒股的入门知识关于KDJ指标的分析。
# 第一个方面，KD指标的背离。在KD处在高位或低位，如果出现与股价走向的背离，则是采取行动的信号。
# 第二个方面，J指标取值超过100和低于0，都属于价格的非正常区域，大于100为超买，小0为超卖。
# 第三个方面，KD的取值。KD的统一取值范围是0～100，我们可以将其划分为3个区域：80以上为超买区，20以下为超卖区，其余为徘徊区。
#   但是这里股票投资者需要注意的是这种划分只是一个信号提示，不能完全按这种分析的方法进行操作。
# 第四个方面，KD指标的交叉。K与D的关系就如同股价与MA的关系一样，也有死亡交叉和黄金交叉的问题。
# 第五个方面，KD指标曲线的形态。当KD指标在较高或较低的位置形成了头肩形和多重顶(底)时，是采取行动的信号。
#   这里股票投资者同样需要注意的是，这些形态一定要在较高位置或较低位置出现，位置越高或越低，结论越可靠。

# 1.当KDJ三个值整体处于20以下的话，显示市场为超卖区；
#   如果整体处于80以上的话则代表市场显示为超买区；
#   在这之间的话则显示买卖平衡，变化趋势不是很明显。
# 2.如果KDJ三者的值都位于50以上的话，则市场此时显示为多头市场，行情有上涨的趋势；
#   如果三者都位于50以下的话，则显示为空头市场，行情有下降的趋势。
# 3.当K值大于D值的时候，行情趋势上涨，
#   K线向上突破D线时，显示买入的信号，
#   反之K线向下突破D值的时候，显示卖出的信号。
# 4.当KDJ指标与K线图的走势呈现相反的趋势，
#   代表行情可能即将出现反转的信号，
#   另当K线和D线的变化趋势突然减弱也是行情反转的预期信号。
# 5.根据KDJ三条线的移动速度可以判断D线的市场敏感度比较小，J线的市场敏感度是最大的
# 判断kdl形态
def judge_form(boll_list):
    print('判断boll形态')
    # kdl_info = {'rq': rq, 'ln': ln, 'hn': hn, 'cn': cn, 'rsv': rsv, 'k': k, 'd': d, 'j': j}
    form = ''
    msg = ''
    form_info = {'form': form, 'msg': msg}
    if len(boll_list) < 3:
        return form_info
    bool_info0 = boll_list[0]
    bool_info1 = boll_list[1]
    bool_info2 = boll_list[2]
    k0 = bool_info0['k']
    d0 = bool_info0['d']
    j0 = bool_info0['j']
    k1 = bool_info1['k']
    d1 = bool_info1['d']
    j1 = bool_info1['j']
    # 按一般划分标准，K、D、J这三值在20以下为超卖区，是买入信号；
    # K、D、J这三值在80以上为超买区，是卖出信号；
    # K、D、J这三值在20 - 80之间为徘徊区，宜观望
    if k0 < 50 and d0 < 50 and j0 < 50:
        if k0 < 20 and d0 < 20 and j0 < 20:
            form += ':3L-'
            msg += '~kdl都位于20以下,超卖区,买入信号'
        else:
            form += ':3L'
            msg += '~kdl都位于50以下,空头市场,下降趋势中,观望'
        if j0 > d0 and k0 > d0 and j1 < d1 and k1 < d1:  # 黄金交叉
            # 当股票经过一段很长时间的低位盘整行情，并且K、D、J三线都处于50线以下时，一旦J线和K线几乎同时向上突破D线时，
            # 表明股市即将转强，股票跌势己经结束，将止跌朝上，可以开始买进，进行中长线建仓。这是KDJ指标“黄金交叉”的一种形式，
            # 金叉出现的位置应该比较低，是在超卖区的位置，越低越好。
            form += ':/XXX1'
            msg += '~注意很长时间的低位盘整行情中,黄金交叉1'
    if k0 > 50 and d0 > 50 and j0 > 50:
        if k0 > 80 and d0 > 80 and j0 > 80:
            form += ':3T+'
            msg += '~kdl都位于80以上,超买区,卖出信号'
        else:
            form += ':3T'
            msg += '~kdl都位于50以上,多头市场,上涨趋势中,观望'

    if k0 > d0:
        if k1 < d1:
            form += ':k/d'
            msg += '~K线向上突破D线，买入信号'
        if k1 >= d1:
            form += ':k>d'
            msg += '~K值大于D值,趋势上涨'
    # 黄金交叉2
    # 当股票经过一段时间的上升过程中的盘整行情，并且K、D、J线都处于50线附近徘徊时，一旦J线和K线几乎同时再次向上突破D线，成交量再度放出时，
    # 表明股市处于一种强势之中，股价将再次上涨，可以加码买进或持股待涨，这就是KDJ指标“黄金交叉”的又一种形式。
    # 有时候K线和D线在低位要交叉好几回，交叉最少二次以上才是买进信号，越多越好。
    if is_in(k0, 55, 45) and is_in(d0, 55, 45) and is_in(j0, 55, 45):
        if j0 > d0 and k0 > d0 and j1 < d1 and k1 < d1:  # 黄金交叉2
            form += ':/XXX2'
            msg += '~注意成交量再度放出时,黄金交叉2,有时候K线和D线在低位要交叉好几回，交叉最少二次以上才是买进信号，越多越好'
    form_info = {'form': form, 'msg': msg}
    return form_info


# 死亡交叉1
# K线在D线已经抬头向上才与D线相交，比D线还在下降时与之相交要可靠得多，换句话说：右侧相交比左侧相交好。当股票经过前期一段很长时间的上升行情后，
# 股价涨幅已经很大的情况下，一旦J线和K线在高位（80以上）几乎同时向下突破D线时，表明股市即将由强势转为弱势，
# 股价将大跌，这时应卖出大部分股票而不能买进，这是KDJ指标的“死亡交叉”的一种形式。

# 死亡交叉2
# 死叉的形成条件与金叉的情况相反，与金叉一样，符合右侧相交原则。当股票经过一段时间的下跌后，而股票向上反弹的动力缺乏，各种均线对股价形成较强的压力时，
# 一旦J线和K线再次向下突破D线时，表明股市将再次进入极度弱市中，股价还将下跌，可以再卖出股票或观望，这是KDJ指标“死亡交叉”的另一种形式。

# 顶背离
# 当股票K线图上的股价走势一峰比一峰高，股价在一直向上涨，而KDJ曲线图上的KDJ指标的走势是在高位一峰比一峰低，这叫顶背离现象。
# 顶背离现象一般是股价将高位反转的信号，表明股价中短期内即将下跌，是卖出的信号

# 底背离
# 当股票K线图上的股价走势一峰比一峰低，股价在向下跌，而KDJ曲线图上的KDJ指标的走势是在低位一底比一底高，这叫底背离现象。
# 底背离现象一般是股价将低位反转的信号，表明股价中短期内即将上涨，是买入的信号。

def is_in(num, max, min):
    if num < max and num > min:
        return 1
    else:
        return 0


# 获取 kdl 最多支持天数100天
# start_rq : 开始日期 格式'2020-11-02' 倒序
# size   : 开始日期往前几天
def get_kdl_interval(code, start_rq, size):
    # n日RSV =（Cn－Ln）÷（Hn－Ln）×100
    # 说明: 公式中，Cn为第n日收盘价；Ln为n日内的最低价；Hn为n日内的最高价。RSV值始终在1—100间波动。
    # 举例: 假如今天为9号, 则Cn为9号的收盘价, Ln则为1 - 9 号的最低价,
    # Hn则为1 - 9号的最高价, 很多人计算错了往往就是这个RSV的值计算是错的.
    day_k_result = k_util.day_k_local_frequency(code, 150, 1200)  # 2十分钟更新一次
    kdj_list = []
    day_k_result_len = len(day_k_result)
    if day_k_result_len < 9:  # todo whm 优化
        return kdj_list
    day_k_use = []
    begin_flag = 0
    for i in range(day_k_result_len):
        day_k = json_util.info_to_json(day_k_result[i])
        if begin_flag == 1:
            day_k_use.append(day_k)
            continue
        if day_k['rq'] == start_rq:
            begin_flag = 1
            day_k_use.append(day_k)
    k = 50
    d = 50
    # 双指针
    length = len(day_k_use)
    for i in range(length - 9):
        ln = 10000
        hn = 0
        rq = json_util.info_to_json(day_k_use[length - i - 10])['rq']
        cn = float(json_util.info_to_json(day_k_use[length - i - 10])['sp'])
        for j in range(9):
            day_k = day_k_use[length - i - 10 + j]
            min = float(json_util.info_to_json(day_k)['min'])
            max = float(json_util.info_to_json(day_k)['max'])
            if min < ln: ln = min
            if max > hn: hn = max
        # RSV =（C－L9）÷（H9－L9）×100
        # 当日K值 = 2 / 3×前一日K值＋1 / 3×当日RSV
        # 当日D值 = 2 / 3×前一日D值＋1 / 3×当日K值
        # J=3D—2K
        rsv = (cn - ln) / (hn - ln) * 100
        k = 2 / 3 * k + 1 / 3 * rsv
        d = 2 / 3 * d + 1 / 3 * k
        j = 3 * k - 2 * d
        kdl_info = {'rq': rq, 'ln': ln, 'hn': hn, 'cn': cn, 'rsv': rsv, 'k': k, 'd': d, 'j': j}
        if i >= length - 9 - size:
            kdj_list.append(kdl_info)
    kdj_list.reverse()
    return kdj_list


# 获取 kdl 最多支持天数100天
# start_rq : 开始日期 格式'2020-11-02' 倒序
# size   : 开始日期往前几天
def get_kdl_rt(code, start_rq, size, rt_price):
    # n日RSV =（Cn－Ln）÷（Hn－Ln）×100
    # 说明: 公式中，Cn为第n日收盘价；Ln为n日内的最低价；Hn为n日内的最高价。RSV值始终在1—100间波动。
    # 举例: 假如今天为9号, 则Cn为9号的收盘价, Ln则为1 - 9 号的最低价,
    # Hn则为1 - 9号的最高价, 很多人计算错了往往就是这个RSV的值计算是错的.
    day_k_result = k_util.day_k_local_frequency(code, 150, 1200)  # 2十分钟更新一次
    kdj_list = []
    day_k_result_len = len(day_k_result)
    if day_k_result_len < 9:  # todo whm 优化
        return kdj_list
    day_k_use = []
    begin_flag = 0
    for i in range(day_k_result_len):
        day_k = json_util.info_to_json(day_k_result[i])
        if begin_flag == 1:
            day_k_use.append(day_k)
            continue
        if day_k['rq'] == start_rq:
            begin_flag = 1
            day_k_use.append(day_k)
    k = 50
    d = 50
    # 双指针
    length = len(day_k_use)
    for i in range(length - 9):
        ln = 10000
        hn = 0
        rq = json_util.info_to_json(day_k_use[length - i - 10])['rq']
        cn = float(json_util.info_to_json(day_k_use[length - i - 10])['sp'])
        for j in range(9):
            day_k = day_k_use[length - i - 10 + j]
            min = float(json_util.info_to_json(day_k)['min'])
            max = float(json_util.info_to_json(day_k)['max'])
            if min < ln: ln = min
            if max > hn: hn = max
        # RSV =（C－L9）÷（H9－L9）×100
        # 当日K值 = 2 / 3×前一日K值＋1 / 3×当日RSV
        # 当日D值 = 2 / 3×前一日D值＋1 / 3×当日K值
        # J=3D—2K
        rsv = (cn - ln) / (hn - ln) * 100
        k = 2 / 3 * k + 1 / 3 * rsv
        d = 2 / 3 * d + 1 / 3 * k
        j = 3 * k - 2 * d
        kdl_info = {'rq': rq, 'ln': ln, 'hn': hn, 'cn': cn, 'rsv': rsv, 'k': k, 'd': d, 'j': j}
        if i >= length - 9 - size:
            kdj_list.append(kdl_info)
    kdj_list.reverse()
    return kdj_list


# 生成下一日的kdl
def gen_the_next_kdl(pre_kdl):
    print('')


# 使用方法
# 1.K与D值永远介于0到100之间。D大于80时，行情呈现超买现象。D小于20时，行情呈现超卖现象。
# 2.上涨趋势中，K值小于D值，K线向上突破D线时，为买进信号。下跌趋势中，K值大于D值，K线向下跌破D线时，为卖出信号。
# 3.KD指标不仅能反映出市场的超买超卖程度，还能通过交叉突破发出买卖信号。
# 4.KD指标不适于发行量小、交易不活跃的股票，但是KD指标对大盘和热门大盘股有极高准确性。
# 5.当随机指标与股价出现背离时，一般为转势的信号。
# 6.K值和D值上升或者下跌的速度减弱，倾斜度趋于平缓是短期转势的预警信号。

if __name__ == '__main__':
    t1 = int(round(time.time() * 1000))
    kdl_list1 = get_kdl_interval('000333', '2020-12-18', 100)
    t2 = int(round(time.time() * 1000))
    print(t2 - t1)
    for i in range(10):
        print(kdl_list1[i])
    print()
